#pragma once
#include"Common.hpp"
class PageCache
{
public:
    static PageCache* GetInstance()
    {
        return &_sInst;
    }
    Span* NewSpan(size_t k)
    {
        if(k>NPAGES-1)
        {
            void*ptr=SystemAlloc(k);
            Span* span=new Span;
            span->_pageId=(PAGE_ID)ptr>>PAGE_SHIFT;
            span->_n=k;
            _idSpanMap[span->_n]=span;
            return span;
        }
        Span* it=_freeLists[k].Begin();
        while(it!=_freeLists[k].End())
        {
            if(it->_freeList!=nullptr)
            {
                return it;
            }
            else
            {
                it=it->_next;
            }
        }
        
        for(size_t i=k+1;i<NPAGES;i++)
        {
            if(!_freeLists[i].Empty())
            {
                Span* nSpan=_freeLists[i].PopFront();
                Span* kSpan=new Span;
                kSpan->_pageId=nSpan->_pageId;
                kSpan->_n=k;

                nSpan->_pageId+=k;
                nSpan->_n-=k;
                _freeLists[i-k].PushFront(nSpan);

                for(PAGE_ID i=0;i<k;i++)    //将申请的内存进行标记，方便回收的时候合并内存
                {
                    _idSpanMap[kSpan->_pageId+i]=kSpan;
                }
                return kSpan;
            }   
        }

        Span* bigSpan= new Span;        //走到这里说明pageCache没有比申请的内存更大的内存块了
        void* ptr=SystemAlloc(NPAGES-1);
        bigSpan->_pageId=(PAGE_ID)ptr>>PAGE_SHIFT;
        bigSpan->_n=NPAGES-1;
        _freeLists[NPAGES-1].PushFront(bigSpan);
        return NewSpan(k);
    }
    Span* MapObjectToSpan(void* obj)
    {
        PAGE_ID id=((PAGE_ID)obj>>PAGE_SHIFT);  //进行强转
        auto ret=_idSpanMap.find(id);
        if(ret!=_idSpanMap.end())
        {
            return _idSpanMap[id];
        }
        else
        {
            assert(false);
            return nullptr;
        }
    }
    void ReleaseSpanToPageCache(Span* span)
    {
        if(span->_n>NPAGES-1)
        {
            void*ptr=(void*)(span->_pageId<<PAGE_SHIFT);
            SystemFree(ptr);
            delete span;
            return;
        }
        while(1)    //合并前后相邻且没有被使用的页再插入到更大页的链表中
        {
            auto ret=_idSpanMap.find(span->_pageId-1);
            if(ret==_idSpanMap.end())
            break;
            Span* prevSpan=ret->second;
            if(prevSpan->_isUse)
            break;
            if(span->_n+prevSpan->_n>NPAGES-1)
            break;
            span->_n+=prevSpan->_n;
            span->_pageId=prevSpan->_pageId;
            _freeLists[prevSpan->_n].Erase(prevSpan);
            delete prevSpan;    //合并页后把被合并的页的空间释放掉
        }
        
        while(1)
        {
            auto ret=_idSpanMap.find(span->_pageId+span->_n);
            if(ret==_idSpanMap.end())
            break;
            Span* nextSpan=ret->second;
            if(nextSpan->_isUse)
            break;
            if(span->_n+nextSpan->_n>NPAGES-1)
            break;
            span->_n+=nextSpan->_n;
            _freeLists[nextSpan->_n].Erase(nextSpan);
            delete nextSpan;
        }
        _freeLists[span->_n].PushFront(span);
        span->_isUse=false;
        _idSpanMap[span->_pageId]=span;
        _idSpanMap[span->_pageId+span->_n-1]=span;
    }
public:
    mutex _pageMtx;
private:
    SpanList _freeLists[NPAGES];
    std::unordered_map<PAGE_ID,Span*>_idSpanMap;
    PageCache(){};
    PageCache(const PageCache& pagecache)=delete;
    static PageCache _sInst;
};